package org.simpleFramework.core;

import com.bird.extension.pattern.abstractfactory.KeyBoard;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.simpleFramework.core.annotation.Component;
import org.simpleFramework.core.annotation.Controller;
import org.simpleFramework.core.annotation.Repository;
import org.simpleFramework.core.annotation.Service;
import org.simpleFramework.core.utils.ClassUtil;
import org.simpleFramework.core.utils.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: sijunqiang
 * @Date: 2020/4/12 23:21
 * @Description: 定义bean的容器
 * <p>
 * 容器的组成部分：
 * 1：保存class的对象及其实例的载体
 * 2：容器的加载
 * 2.1 配置的管理与获取
 * 2.2 获取指定范围内的Class对象
 * 2.3 依据配置提取Class对象, 连同实例一并存入容器
 * 3：容器的操作方式
 */

@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE) //access = AccessLevel.PRIVATE 标记空参构造只能是私有的 外部普通是创建对象是无法创建的
public class BeanContainer {

    /**
     * 第一步: 存放所有被配置标记的目标对象map
     * concurrentHashMap的并发性很好
     */
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();

    /**
     * 第二步： 加载bean的注解实例列表,这里面保存的都是注解
     **/
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);


    /**
     * <P第三步：扫面并加载所有的beans</P>
     *
     * @param packageName 包名字
     */
    public synchronized void loadBeans(String packageName) {
        //判断bean容器是否已经被加载过
        if (isLoaded()) {
            log.warn("beanContainer 容器已经被加载过了");
            return;
        }
        //获取所有的class实例
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("没有从该包下面获取实例：packageName：{}", packageName);
            return;
        }
        //遍历每个实例
        for (Class<?> clazz : classSet) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                //若果类上面标记了定义好的注解
                if (clazz.isAnnotationPresent(annotation)) {
                    //将目标类本身作为键, 目标类的实例作为值,放入到beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        //容器加载完成
        loaded = true;
    }


    /**
     * 因为容器加载比较慢,耗时 所以设置这个属性
     * 容器是否已经加载过bean
     */
    private boolean loaded = false;

    /**
     * 是否已经加载过bean
     * true 是
     * false 不是
     *
     * @return
     */
    public boolean isLoaded() {
        return loaded;
    }


    /**
     * <P>bean实例的数量</P>
     *
     * @return 数量
     */
    public int size() {
        return beanMap.size();
    }


    /***************下面的方法容器启动时创建容器的单例 (使用枚举类的方式创建实例)***************************************************************/
    //<P>获取bean容器的实例</P>   这个方法是：线程安全并且可以抵御反射和序列化的单例容器
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    // <P>私有枚举类</P>  note: 都是static修饰的 而且线程安全的
    private enum ContainerHolder {
        /**
         *
         */
        HOLDER;
        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }
    }


    public static Boolean main(String[] args) {


        String code = "";
        if (isaBoolean(code)) return true;


        boolean hha = "" == code;

        return false;
    }

    private static boolean isaBoolean(String code) {
        return code == null || code.isEmpty();
    }


    //**********************************下面是供外界对容器的操作方式**********************************************

    /**
     * 第一个方法: 添加bean实例到容器中
     * <P> 添加class对象和其实例</P>
     *
     * @param clazz Class 对象
     * @param bean  Object  Bean实例
     * @return bean实例 没有返回null
     */
    public Object addBean(Class<?> clazz, Object bean) {
        Object value = beanMap.put(clazz, bean);
        return value;
    }


    /**
     * 第二个方法：删除容器中的bean实例
     *
     * @param clazz class 对象
     * @return 删除之后的Bean实例 没有返回null
     */
    public Object removeBean(Class<?> clazz) {
        Object value = beanMap.remove(clazz);
        return value;
    }


    /**
     * 第三个方法：根据Class对象获取bean实例
     *
     * @param clazz class对象
     * @return 该class对应的实例
     */
    public Object getBean(Class<?> clazz) {
        Object bean = beanMap.get(clazz);
        return bean;
    }

    /**
     * 第四个方法： 获取容器管理的所有的Class对象集合(就是获取所有的key)
     *
     * @return Class集合
     */
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classSet = beanMap.keySet();
        return classSet;
    }


    /**
     * 第五个方法：获取所有的Bean集合
     *
     * @return Bean集合
     */
    public Set<Object> getBeans() {
        Collection<Object> values = beanMap.values();
        Set<Object> objects = new HashSet<>(values);
        return objects;
    }


    /**
     * 第六个方法： 根据注解筛选出Bean的 class集合
     *
     * @param annotation annotation 注解 Class对象
     * @return Class集合
     */
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
        //1：获取beanMap中的所有的class对象
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("集合为空");
            return null;
        }

        // 通过注解筛选出被注解标记的class对象,并添加classSet里面
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz : keySet) {
            //类是否有相关的注解
            if (clazz.isAnnotationPresent(annotation)) {
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0 ? classSet : null;
    }


    /**
     * 第七个方法： 通过接口或者是父类 获取实现类或者子类的Class集合,不包括其本身
     *
     * @param interfaceOrClass 接口的 Class或者父类的Class
     * @return Class集合
     */
    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass) {

        //1：获取beanMap的所有的Class对象(就是key)
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("集合为空");
            return null;
        }

        //2：判断keySet里面的元素是否是传入的接口或者是类的子类, 是 就将其添加新的classSet集合中
        Set<Class<?>> classSet = new HashSet<>();
        // 1.1 遍历获取每一个key
        for (Class<?> clazz : keySet) {
            // 1.2 判断keySet集合里面是的元素是否是传入的接口或者是类的子类
            if (interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)) {
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0 ? classSet : null;
    }


}
